package com.jzo2o.market.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.utils.*;
import com.jzo2o.market.constants.RedisConstants;
import com.jzo2o.market.constants.TabTypeConstants;
import com.jzo2o.market.enums.ActivityStatusEnum;
import com.jzo2o.market.enums.ActivityTypeEnum;
import com.jzo2o.market.mapper.ActivityMapper;
import com.jzo2o.market.model.domain.Activity;
import com.jzo2o.market.model.domain.Coupon;
import com.jzo2o.market.model.domain.CouponWriteOff;
import com.jzo2o.market.model.dto.request.ActivityQueryForPageReqDTO;
import com.jzo2o.market.model.dto.request.ActivitySaveReqDTO;
import com.jzo2o.market.model.dto.response.ActivityInfoResDTO;
import com.jzo2o.market.service.IActivityService;
import com.jzo2o.market.service.ICouponService;
import com.jzo2o.market.service.ICouponWriteOffService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static com.jzo2o.market.enums.ActivityStatusEnum.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2023-09-16
 */
@Service
public class ActivityServiceImpl extends ServiceImpl<ActivityMapper, Activity> implements IActivityService {
    private static final int MILLION = 1000000;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private ICouponService couponService;

    @Resource
    private ICouponWriteOffService couponWriteOffService;

    @Override
    public void saveActivity(ActivitySaveReqDTO dto) {
        if(ObjectUtils.isNull(dto)){
            throw new BadRequestException("参数不能为空");
        }
        //发放时间开始时间不能小于当前时间
        if(!dto.getDistributeStartTime().isAfter(LocalDateTime.now())){
            throw new BadRequestException("发放时间开始时间不能小于当前时间");
        }
        //发放结束时间不能早于发放开始时间
        if(!dto.getDistributeEndTime().isAfter(dto.getDistributeStartTime())){
            throw new BadRequestException("发放结束时间不能早于发放开始时间");
        }
        //如果满减：
        if(dto.getType().equals(ActivityTypeEnum.AMOUNT_DISCOUNT.getType())){
            //1、折扣金额必须输入
            if(dto.getDiscountAmount()==null){
                throw new BadRequestException("折扣金额必须输入");
            }
            //2、折扣金额必须大于0的整数
            if(dto.getDiscountAmount().compareTo(BigDecimal.ZERO)==-1){
                throw new BadRequestException("折扣金额必须大于0的整数");
            }
        }
        //如果是折扣：
        if(dto.getType().equals(ActivityTypeEnum.RATE_DISCOUNT.getType())) {
            //1、折扣比例必须输入
            if (dto.getDiscountRate() == null) {
                throw new BadRequestException("折扣比例必须输入");
            }
            //2、折扣比例必须大于0，小于100的整数
            if (dto.getDiscountRate() >= 100 || dto.getDiscountRate() <= 0) {
                throw new BadRequestException("折扣比例必须大于0，小于100的整数");
            }
        }
        if(dto.getId()==null){
           //新增
            Activity activity = BeanUtils.copyBean(dto, Activity.class);
            activity.setStatus(ActivityStatusEnum.NO_DISTRIBUTE.getStatus());//设置优惠卷的状态为待生态
            activity.setStockNum(activity.getTotalNum());//设置库存
            this.save(activity);//新增

        }else{
            //修改
           this.lambdaUpdate()
                   .set(dto.getDiscountRate()!=null,Activity::getDiscountRate,dto.getDiscountRate())
                   .set(dto.getDiscountAmount()!=null,Activity::getDiscountAmount,dto.getDiscountAmount())
                   .set(dto.getType()!=null,Activity::getType,dto.getType())
                   .set(dto.getDistributeEndTime()!=null,Activity::getDistributeEndTime,dto.getDistributeEndTime())
                   .set(dto.getDistributeStartTime()!=null,Activity::getDistributeStartTime,dto.getDistributeStartTime())
                   .set(dto.getTotalNum()!=null,Activity::getTotalNum,dto.getTotalNum())
                   .set(dto.getName()!=null,Activity::getName,dto.getName())
                   .set(dto.getAmountCondition()!=null,Activity::getAmountCondition,dto.getAmountCondition())
                   .set(dto.getValidityDays()!=null,Activity::getValidityDays,dto.getValidityDays())
                   .eq(Activity::getId,dto.getId())
                   .update();
        }
    }
    /**
     * 分页查询优惠卷接口
     * @param dto
     * @return
     */
    @Override
    public PageResult<ActivityInfoResDTO> pageQuery(ActivityQueryForPageReqDTO dto) {
        IPage page=new Page(dto.getPageNo(),dto.getPageSize());
        IPage page1 = this.lambdaQuery()
                .eq(dto.getId() != null, Activity::getId, dto.getId())
                .eq(dto.getType() != null, Activity::getType, dto.getType())
                .eq(dto.getStatus() != null, Activity::getStatus, dto.getStatus())
                .like(dto.getName() != null, Activity::getName, dto.getName())
                .orderBy(dto.getOrderBy1()!=null,dto.getIsAsc1(),Activity::getUpdateTime)
                .orderByDesc(Activity::getUpdateTime)
                .page(page);
        List<Activity> records = page1.getRecords();
        PageResult<ActivityInfoResDTO> res=new PageResult<>();
        res.setPages(page1.getPages());
        res.setTotal(page1.getTotal());
        if(CollUtils.isEmpty(records)){
            return res;
        }

        List<ActivityInfoResDTO> list = BeanUtils.copyToList(records, ActivityInfoResDTO.class);
        Set<Long> set = list.stream().map(ActivityInfoResDTO::getId).collect(Collectors.toSet());
        List<ActivityInfoResDTO> receivelist= couponService.selectCountByActivetyId(set);
        Map<Long, Integer> collect = receivelist.stream().collect(Collectors.toMap(ActivityInfoResDTO::getId, ActivityInfoResDTO::getReceiveNum));
        List<ActivityInfoResDTO> list1=couponWriteOffService.selectCountByActivetyId(set);
        Map<Long, Integer> collect1 = list1.stream().collect(Collectors.toMap(ActivityInfoResDTO::getId, ActivityInfoResDTO::getWriteOffNum));
        for (ActivityInfoResDTO resDTO : list) {
            resDTO.setWriteOffNum(collect1.getOrDefault(resDTO.getId(),0));//核销数量
            resDTO.setReceiveNum(collect.getOrDefault(resDTO.getId(),0));//领取数量
        }
        res.setList(list);
        return res;
    }

    @Override
    public ActivityInfoResDTO getActivityById(Long id) {
        Activity activity = this.getById(id);
        ActivityInfoResDTO dto = BeanUtils.copyBean(activity, ActivityInfoResDTO.class);
        Integer count = couponService.lambdaQuery()
                .eq(Coupon::getActivityId, dto.getId())
                .count();
        dto.setReceiveNum(count);
        Integer count1 = couponWriteOffService.lambdaQuery()
                .eq(CouponWriteOff::getActivityId, dto.getId())
                .count();
        dto.setWriteOffNum(count1);
        return dto;
    }

    /**
     * 撤销活动
     * @param id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void revokeActivity(Long id) {
        if(id==null){
            throw new BadRequestException("参数异常");
        }
        Activity activity = this.getById(id);
        if(activity==null){
            throw new BadRequestException("活动不能为空");
        }
        //更新活动状态为作废
        boolean update = this.lambdaUpdate()
                .set(Activity::getStatus, ActivityStatusEnum.LOSE_EFFICACY.getStatus())
                .eq(Activity::getId, id)
                .update();
        if(!update){
            throw new DBException("修改活动失败");
        }
        // 将所有抢到本活动优惠券的状态为未使用的记录的状态更改为“已失效” 。
        boolean update1 = couponService.lambdaUpdate()
                .set(Coupon::getStatus, 3)
                .eq(Coupon::getStatus, 1)
                .eq(Coupon::getActivityId, id)
                .update();
        if(!update1){
            throw new DBException("修改领取优惠卷失败");
        }
    }

    @Override
    public void preHeat() {
        List<Activity> list = this.lambdaQuery()
                .in(Activity::getStatus, ActivityStatusEnum.NO_DISTRIBUTE.getStatus(), DISTRIBUTING.getStatus())
                .lt(Activity::getDistributeStartTime, DateUtils.now().plusDays(30))
                .orderByDesc(Activity::getDistributeStartTime)
                .list();
        if(CollUtils.isEmpty(list)) {
          list=new ArrayList<>();//防止缓存击穿
        }
        List<ActivityInfoResDTO> dtoList = BeanUtils.copyToList(list, ActivityInfoResDTO.class);

       String jsonStr = JsonUtils.toJsonStr(dtoList);
       redisTemplate.opsForValue().set(RedisConstants.RedisKey.ACTIVITY_CACHE_LIST,jsonStr);

       //将库存存放到redis中
        //未开始的活动直接存入redis中
        dtoList.stream().filter(x->ObjectUtils.equals(NO_DISTRIBUTE.getStatus(),getCurrentStatus(x)))
                .forEach(x->{
                 String key=String.format(RedisConstants.RedisKey.COUPON_RESOURCE_STOCK,x.getId()%10);
                 String hashKey=x.getId().toString();
                 Object value = x.getStockNum();
                 redisTemplate.opsForHash().put(key,hashKey,value);
                });
       //进行中的将没有加入的放入dis缓存中
      dtoList.stream().filter(x->ObjectUtils.equals(DISTRIBUTING.getStatus(),getCurrentStatus(x)))
                .forEach(x->{
                    String key=String.format(RedisConstants.RedisKey.COUPON_RESOURCE_STOCK,x.getId()%10);
                    String hashKey=x.getId().toString();
                    Object value = x.getStockNum();
                    redisTemplate.opsForHash().putIfAbsent(key,hashKey,value);
                });
    }

    @Override
    public List<ActivityInfoResDTO> queryForPage(Integer tabType) {
        //获取缓存中的数据
        String key=RedisConstants.RedisKey.ACTIVITY_CACHE_LIST;
        Object o = redisTemplate.opsForValue().get(key);
        List<ActivityInfoResDTO> list = JsonUtils.toList(o.toString(), ActivityInfoResDTO.class);
        if(CollUtils.isEmpty(list)){
            return CollUtils.emptyList();
        }
        //获取用户想要的优惠券状态
        Integer selectStatus=ObjectUtils.equal(tabType, TabTypeConstants.SEIZING)?DISTRIBUTING.getStatus():NO_DISTRIBUTE.getStatus();
        //筛选所选状态
        List<ActivityInfoResDTO> dtoList = list.stream().filter(x -> ObjectUtils.equals(selectStatus, getCurrentStatus(x)))
                .peek(x -> x.setRemainNum(x.getStockNum()))
                .collect(Collectors.toList());
        return dtoList;
    }

    public Integer getCurrentStatus(ActivityInfoResDTO x) {
        //状态为未开始，且当前时间大于开始时间小于结束时间，返回状态进行中，并修改活动状态
        if(ObjectUtils.equals(x.getStatus(),NO_DISTRIBUTE.getStatus())&&DateUtils.now().isAfter(x.getDistributeStartTime())&&DateUtils.now().isBefore(x.getDistributeEndTime())){
            x.setStatus(DISTRIBUTING.getStatus());
            return DISTRIBUTING.getStatus();
        }else if(ObjectUtils.equals(x.getStatus(),NO_DISTRIBUTE.getStatus())&&DateUtils.now().isAfter(x.getDistributeStartTime())){
            //状态为未开始，且当前时间大于结束时间
            x.setStatus(LOSE_EFFICACY.getStatus());
            return LOSE_EFFICACY.getStatus();
        }else if(ObjectUtils.equals(x.getStatus(),DISTRIBUTING.getStatus())&&DateUtils.now().isAfter(x.getDistributeEndTime())){
            //状态为进行中，且当前时间大于结束时间
            x.setStatus(LOSE_EFFICACY.getStatus());
            return LOSE_EFFICACY.getStatus();
        }
        return x.getStatus();
    }

    @Override
    public boolean decrStockNum(Long activityId) {
        boolean update = this.lambdaUpdate()
                .setSql("stock_num=stock_num-1")
                .eq(Activity::getId, activityId)
                .gt(Activity::getStockNum, 0)
                .update();
        return update;
    }
}
